AWS AppSync+Lambda Resolverでエラー処理
吉川です。
早速ですが、AppSync+Lambdaリゾルバにおいて、(VTLではなく)Lambda関数からAppSync経由でクライアントへ errors
を返すにはどのようなコードを書けば良いのかについて調べたので共有します。
結論から言うと、 callback('Error Message.')
もしくは throw new Error('Error Message.')
どちらかのパターンを使うと良さそうでした。
環境
- node 16.13.0
- typescript 3.9.7
- aws-cdk-lib 2.20.0
- @aws-cdk/aws-appsync-alpha 2.20.0-alpha.0
- constructs 10.0.115
前提
CDKコード
import { Construct } from 'constructs' import * as cdk from 'aws-cdk-lib' import * as appsync from '@aws-cdk/aws-appsync-alpha' import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs' export class AppSyncStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props) /** * Backend */ // AppSync API const api = new appsync.GraphqlApi(this, 'myAppsyncApi', { name: 'myAppsyncApi', schema: appsync.Schema.fromAsset('./schema.graphql'), authorizationConfig: { defaultAuthorization: { authorizationType: appsync.AuthorizationType.API_KEY, }, }, }) // Lambda関数 const userFn = new lambdaNodejs.NodejsFunction(this, 'userFn', { entry: 'lambda-handler/find-user-handler.ts', }) // Lambda関数をDataSourceとしてAppSyncAPIと紐付ける const userDs = api.addLambdaDataSource('userDs', userFn) // schema.graphqlで定義した中のどの操作とマッピングするかを指定 userDs.createResolver({ typeName: 'Query', fieldName: 'user', }) } }
スキーマファイル
type User { id: String! name: String! } type Query { user: User! }
投げるクエリ
以下のクエリをAppSyncマネジメントコンソールで投げて検証します。
query MyQuery { user { id name } }
Lambda Resolverのエラーハンドリングパターンを色々試してみる
では、確認したパターンを紹介したいと思います。
callback()に文字列を渡す
callback()
関数にメッセージを渡すことでクライアントにエラーを返すことができます。
import { AppSyncResolverEvent, Callback, Context } from 'aws-lambda' export const handler = async ( event: AppSyncResolverEvent<{}, {}>, _: Context, callback: Callback ) => { console.log(JSON.stringify({ event })) callback('Example error message.') }
{ "data": null, "errors": [ { "path": [ "user" ], "data": null, "errorType": "string", "errorInfo": null, "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "Example error message." } ] }
Errorをthrowする
Error
を throw
することでクライアントにエラーを返すことができます。
import { AppSyncResolverEvent, Callback, Context } from 'aws-lambda' export const handler = async (event: AppSyncResolverEvent<{}, {}>) => { console.log(JSON.stringify({ event })) throw new Error('Example error message.') }
{ "data": null, "errors": [ { "path": [ "user" ], "data": null, "errorType": "Error", "errorInfo": null, "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "Example error message." } ] }
callback()にErrorを渡す
callback()
関数に Error
を渡すことでもクライアントにエラーを返すことができます。
import { AppSyncResolverEvent, Callback, Context } from 'aws-lambda' class CustomError extends Error { constructor(message: string) { super(message) this.name = 'CustomError' } } export const handler = async ( event: AppSyncResolverEvent<{}, {}>, _: Context, callback: Callback ) => { console.log(JSON.stringify({ event })) callback(new Error('Example error message.')) }
{ "data": null, "errors": [ { "path": [ "user" ], "data": null, "errorType": "Error", "errorInfo": null, "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "Example error message." } ] }
カスタムErrorをthrowする
Error
を継承したカスタム Error
も throw
でエラーを返せます。
import { AppSyncResolverEvent, Callback, Context } from 'aws-lambda' class CustomError extends Error { constructor(message: string) { super(message) this.name = 'CustomError' } } export const handler = async (event: AppSyncResolverEvent<{}, {}>) => { console.log(JSON.stringify({ event })) throw new CustomError('Custom error message.') }
{ "data": null, "errors": [ { "path": [ "user" ], "data": null, "errorType": "CustomError", "errorInfo": null, "locations": [ { "line": 2, "column": 3, "sourceName": null } ], "message": "Custom error message." } ] }
this.name = 'CustomError'
を記述しないと errorType
はデフォルトの Error
のままだったのでご注意ください。
なお、割愛しますが「 callback()
にカスタムErrorを渡す」パターンも同じように使うことができました。
[NG] 文字列をreturnする
callback()に文字列を渡すパターン
に準ずるかと思いましたが、こちらはできないようでした。
import { AppSyncResolverEvent, Callback, Context } from 'aws-lambda' export const handler = async (event: AppSyncResolverEvent<{}, {}>) => { console.log(JSON.stringify({ event })) return 'Example error message.' }
{ "data": null, "errors": [ { "path": [ "user", "id" ], "locations": null, "message": "Cannot return null for non-nullable type: 'String' within parent 'User' (/user/id)" }, { "path": [ "user", "name" ], "locations": null, "message": "Cannot return null for non-nullable type: 'String' within parent 'User' (/user/name)" } ] }
[NG] Errorをreturnする
こちらも callback()にErrorを渡すパターン
に準ずるかと思いましたが、できないようでした。
import { AppSyncResolverEvent, Callback, Context } from 'aws-lambda' export const handler = async (event: AppSyncResolverEvent<{}, {}>) => { console.log(JSON.stringify({ event })) return new Error('Example error message.') }
{ "data": null, "errors": [ { "path": [ "user", "id" ], "locations": null, "message": "Cannot return null for non-nullable type: 'String' within parent 'User' (/user/id)" }, { "path": [ "user", "name" ], "locations": null, "message": "Cannot return null for non-nullable type: 'String' within parent 'User' (/user/name)" } ] }
以上、参考になれば幸いです。